trashpandafandomcom-20200213-history
What are dispatch tables?
Ever needed to run different functions depending on the value of a variable? One way of doing this could be a long series of if-elif statements, but there is a better option : a dispatch table. What is it? A dispatch table in Python is basically a dictionary of functions. This concept is not Python-specific, rather quite common in Computer Science: A dispatch table is a table of pointers to functions or methods. Dispatch tables are among the most common approaches in OOP to implement late binding, that is : Looking up by name at runtime the function you want to be called rather than hardcoding it. We are going to look into what that means, how you can and why you should take advantage of it. The if-elif approach Suppose you have a Date object and you need to execute a specific function based on its weekday. One way of doing this could be with if-elif statements : import datetime my_special_day = datetime.date(2019, 5, 25) if my_special_day.weekday() 0: do_monday() elif my_special_day.weekday() 1: do_tuesday() elif my_special_day.weekday() 2: do_wednesday() elif my_special_day.weekday() 3: do_thursday() elif my_special_day.weekday() 4: do_friday() elif my_special_day.weekday() 5: do_saturday() elif my_special_day.weekday() 6: do_sunday() So what is wrong with this approach? : *This code is ugly and inelegant. *It is error-prone. *The code is not robust. As there is no separation between handlers for the various cases, and the whole logic is bound to one big piece of code. *It is inefficient. A chain of if's is an O(n) complexity. Not the worst but it can certainly be better. Functions as first-class citizens In Python, everything is an object. Including functions and methods. Generally speaking, functions are first-class citizens in Python. This means that all operations available to other entities are also available to functions. Such as : *They can be stored in variables *They can be passed as parameters to functions. *They can be returned from functions and methods. and so on. Here is an example of how we can play around with functions passing them around as if they were normal variables : def foo(name): print("I'm {}, I solve problems".format(name)) foo("Windston Wolf") >>> I'm Winston Wolf, I solve problems bar = foo bar("Renee Marsland") >>> I'm Renee Marsland, I solve problems The key thing to notice here is where we assign the function foo to the variable bar, and from that point on we can use bar() as an alias of foo(). In fact, there is a huge difference between foo() and foo : The former being a function call that asks Python to execute the function; the latter being the object in memory representing the function itself. Therefore, we could even pass a function as a parameter of another function's argument: def foo(func): func() def bar(): print("Hi again!") foo(bar) >>> Hi again! Here we are passing a function to another function and invoking and executing it from the scope of the called function. Plus the whole concept of decorators is possible thanks to this feature. Dispatch tables in Python So how can we use this first-class feature to build a dispatch table? We use dictionaries to do this. Here is an example of a dispatch table to solve the same problem as our my_special_day code aboce : import datetime dispatch = { 0: do_monday, 1: do_tuesday, 2: do_wednesday, 3: do_thursday, 4: do_friday, 5: do_saturday, 6: do_sunday } my_special_day = datetime.date(2019, 5, 25) dispatchmy_special_day.eekday()() Here we are assigning each function to a key we find convenient, in this case the result of the weekday() method on Date objects, then we use the dispatch dictionary to retrieve the object associated to the function and then ask Python to execute the function by appending the () . This approach is far better than the previous method for several important reasons : *'Less and cleaner code', more readable and no need to add a long set of if-elif statements. *Code is far more robust. The handlers for the various type are properly separated. The whole dispatch mechanism doesn't need to know anything specific about the handlers. *The dispatch mechanism is independent from the code using it. Although it is not the case for our specific example we used, if you need to enable more functions or disable existing ones, you just need to make a small change to the dispatch dictionary without altering the logic itself. This loose coupling is often a desirable design pattern in software engineering. *And lastly, we are converting an O(n) to a O(1) algorithm. Dictionaries are hash tables in Python, so the look-up process takes a constant time, while the if-elif compound need a linear scan across the whole set of statements. References Dispatch Tables in Python by Andrea Colangelo Python's Functions Are First-Class